package com.reebake.ideal.protect.core;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.reebake.ideal.protect.exception.ProtectHeaderNotFoundException;
import com.reebake.ideal.protect.properties.ProtectProperties;
import com.reebake.ideal.servlet.core.ResponseBodyWrapper;
import com.reebake.ideal.servlet.util.WebUtil;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpMethod;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import java.io.IOException;

@Getter
@RequiredArgsConstructor
public abstract class AbstractProtectFilter extends OncePerRequestFilter {
    public static final String HEADER_NAME_PROTECT = "X-PROTECT";
    private final ProtectProperties protectProperties;

    private boolean checkUseProtect(HttpServletRequest request) {
        if(request instanceof MultipartHttpServletRequest
                || HttpMethod.OPTIONS.matches(request.getMethod())
                || WebUtil.isEventStreamRequest(request)) {
            return false;
        }
        if(CollectionUtil.isNotEmpty(protectProperties.getIgnored())) {
            for(String ignore : protectProperties.getIgnored()) {
                AntPathMatcher pathMatcher = new AntPathMatcher();
                if(pathMatcher.match(ignore, request.getRequestURI())) {
                    return false;
                }
            }
        }
        String filterName = this.getClass().getSimpleName();
        if(!protectProperties.getProtectTypes().contains(filterName)) {
            return false;
        }
        return true;
    }

    public ProtectParameter getProtectParameter(HttpServletRequest request) {
        String headerParam = request.getHeader(HEADER_NAME_PROTECT);
        if(StrUtil.isBlank(headerParam)) {
            throw new ProtectHeaderNotFoundException();
        }
        String[] paramArray = headerParam.split(StrPool.BACKSLASH + StrPool.DOT);
        ProtectParameter protectParameter = JSONUtil.toBean(Base64.decodeStr(paramArray[0]), ProtectParameter.class);

        protectParameter.setParams(paramArray[0]);
        protectParameter.setSignature(paramArray[1]);
        return protectParameter;

    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if(!checkUseProtect(request)) {
            filterChain.doFilter(request, response);
            return;
        }
        try {
            protect(request, response, filterChain);
        }catch (Exception e) {
            handleException(response, e);
        }
    }

    public void handleException(HttpServletResponse response, Exception e) {
        ResponseBodyWrapper responseBodyWrapper = SpringUtil.getBean(ResponseBodyWrapper.class);
        if(ObjUtil.isNull(responseBodyWrapper)) {
            return;
        }
        responseBodyWrapper.convertAndWrite(response, e);
    }

    protected abstract void protect(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException;
}
